<template>
  <div
    class="flexbox form__box"
    :style="{ width: width || '100%' }"
    ref="selectFlexBox"
    @click="forbidBubbling"
  >
    <div
      class="shadeing"
      v-if="(setObj.choice == true || setObj.tree == true) && show == true"
      @click="showOpen($event, false)"
    ></div>
    <div class="title form__title" v-if="title" :style="{ width: title_width || '' }">
      <span class="red" v-if="emphasize">*</span>
      <slot name="title"></slot>
      {{ title }}
      <slot name="titleAfter"></slot>
    </div>
    <div class="flex" :style="{ 'z-index': show == true ? 199:1, background:backgroundColor }">
      <!-- <input type="hidden" v-model="currentValue"> -->
      <div
        class="select__box"
        :class="{ select__boxAcv: show == true, select__choice: setObj.choice == true }"
        @click="showOpen($event, true)"
        ref="selectBox"
      >
        <div
          class="select__placeholder"
          v-if="textValue == undefined ||(textValue == '' && setObj.choice != true) || (choiceSelect.length == 0 && setObj.choice == true && choiceTextValue == '')"
        >{{ placeholder || '请选择' }}</div>
        <!-- 多选 -->
        <ul class="select-choice__ul" v-if="setObj.choice == true">
          <li class="item" v-for="(item, index) in choiceSelect" :key="index">
            {{ item[setObj.text] }}
            <span class="remove" @click="remove($event, item[setObj.code])">
              <i class="iconfont icon-shanchuguanbiquxiaow transition__all_3s"></i>
            </span>
          </li>
          <li class="fl">
            <input
              type="text"
              v-model="choiceTextValue"
              @input="choiceChange"
              class="select__input"
              ref="selectInput"
              :style="{ width: choiceWidth + 'px' }"
            />
          </li>
        </ul>
        <!-- 树 -->
        <input
          type="text"
          v-model="textValue"
          readonly
          class="select__input"
          ref="selectInput"
          v-else-if="setObj.tree == true"
        />
        <!-- 单选时输入框 -->
        <input
          type="text"
          v-model="textValue"
          @blur="selectOddBlur($event)"
          @input="search"
          class="select__input"
          ref="selectInput"
          v-else
        />
        
      </div>
      <slot name="after"></slot>
      <div
        class="select__opacityBox"
        :style="{ width: domElm.width + 'px', left: domElm.left + 'px', top: domElm.top + 'px',zIndex:999 }"
        ref="selectWin"
      >
        <div
          class="select__opacity transition__all_3s"
          :style="{ opacity: show == true ? 1:0, top: where == 'down' ? choiceTop + 'px':'', bottom: where == 'up' ? choiceBottom + 'px':'' }"
        >
          <div
            class="select__listbox transition__all_3s"
            :style="{ height: show == true ? height + 'px':0 }"
          >
            <div
              class="select__list"
              :style="{ height: height + 'px' }"
              ref="selectList"
              @scroll="scroll($event)"
            >
              <div v-if="setObj.tree == true">
                <div class="select-tree__search flexbox">
                  <input
                    type="text"
                    v-model="treeTextValue"
                    placeholder="请输入检索关键字"
                    class="select__input"
                  />
                  <div class="select-tree__searchBtn transition__all_3s" @click="clear">清空</div>
                </div>
                <div class="hidden scroll_y" :style="{ height: (height - 31) + 'px' }">
                  <!-- tree -->
                  <ts-tree
                    :source="treeObj"
                    width="100%"
                    height="auto"
                    :row-id="setObj.code"
                    :expandAll="setObj.treeExpand===false?false:true"
                    :search-words="treeTextValue"
                    :checkRows="checkRowKeys"
                    @change="treeChange"
                    ref="tree"
                  ></ts-tree>
                </div>
              </div>
              <!-- 下拉九宫格 -->
              <div v-else-if="setObj.grid == true">
                <div class="hidden scroll_y select_grid_contanier" :style="{ height: height+'px' }">
                  <ul style="padding: 5px 15px;">
                    <li
                      v-for="(item,index) in data"
                      :key="index"
                      :style="'width: '+(setObj.gridLiWidth||'25%;')"
                      :title="item[setObj.text]"
                    >
                      <vxe-checkbox
                        v-model="item.selected"
                        v-if="setObj.choice"
                        @change="choice(item,item[setObj.code])"
                      >{{ item[setObj.text] }}</vxe-checkbox>
                      <span
                        v-else
                        @mousedown.stop="links($event, item[setObj.code])"
                      >{{ item[setObj.text] }}</span>
                    </li>
                  </ul>
                </div>
              </div>
              <div v-else>
                <!-- 多选 -->
                <div v-if="setObj.choice == true">
                  <div
                    class="select__item"
                    v-for="(item, index) in data"
                    :key="index"
                    :title="item[setObj.text]"
                  >
                    <vxe-checkbox
                      v-model="item.selected"
                      @change="choice(item,item[setObj.code])"
                    >{{ item[setObj.text] }}</vxe-checkbox>
                  </div>
                  <!-- <div class="select__load" v-if="nomore == true">-- 无更多数据 --</div> -->
                </div>
                <!-- 单选 -->
                <div v-else>
                  <div
                    class="select__item"
                    @mousedown.stop="links($event, '')"
                    v-if="nomore != true && data.length > 0 && clearable"
                  >{{ '请选择' }}</div>
                  <div
                    class="select__item"
                    v-for="(item, index) in data"
                    :key="index"
                    @mousedown.stop="links($event, item[setObj.code])"
                    :title="item[setObj.text]"
                  >{{ item[setObj.text] }}</div>
                </div>
                <div class="select__load" v-if="loading == true">加载中...</div>
                <div class="select__load" v-if="nomore == true">-- 无更多数据 --</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'tsSelect',
  props: {
    value: {
      type: [Number, String, Boolean],
      default: ''
    },
    clearable: {
      type: [Boolean],
      default: true
    },
    source: Object,
    placeholder: String,
    disabled: Boolean,
    emphasize: Boolean,
    title: String,
    width: String,
    checkRows: Array,
    titleWidth: [String, Number]
  },
  data() {
    return {
      backgroundColor: '#fff',
      currentValue: this.value,
      textValue: '',
      choiceTextValue: '',
      checkRowKeys: this.checkRows || [],
      data: [],
      setObj: JSON.parse(JSON.stringify(this.source || {})),
      show: false,
      height: 0,
      page: 1,
      nomore: false,
      loading: false,
      clearTimes: null,
      isSearch: false,
      choiceSelect: [],
      choiceTop: 28,
      choiceBottom: 60,
      choiceWidth: 12,
      treeTextValue: '', // 树检索关键字
      // 层位置等基本属性
      domElm: {
        width: 0,
        left: 0,
        top: 0
      },
      isInit: true,
      where: 'down',
      title_width: '',
      searchWords: '',
      isValue: false,
      isSource: false,
      treeObj: this.treeObjFn(),
      isScroll: true,
      isReq: true,
      blurTF: true,
      isDataRef: true
    }
  },
  computed: {},
  created() {
    const val = this.setObj.value
    const _val = this.value
    let _code = null
    if (typeof val === 'object' && this.setObj.isReq !== true) {
      this.setValue(val.name, val.code)
      if (this.setObj.choice === true && val.name) {
        let _name = val.name.split(',')
        let _code = val.code.split(',')
        _name.forEach((v, i) => {
          let d = {}
          d[this.setObj.text] = v
          d[this.setObj.code] = _code[i]
          d.selected = true
          this.choiceSelect.push(d)
        })
      }
    } else {
      this.isInit = false
    }
    // 初始化是否查询数据
    if (this.setObj.isReq === true) {
      this.initSearchData()
    }
    // 标题宽度计算
    if (this.titleWidth) {
      this.title_width = this.titleWidth.toString()
      if (
        this.title_width.indexOf('px') === -1 &&
        this.title_width.indexOf('%') == -1 &&
        this.title_width !== 'auto'
      ) {
        this.title_width = this.title_width + 'px'
      }
    }
  },
  mounted() {
    const elm = this.$refs.selectBox.getBoundingClientRect()
    this.domElm.width = elm.width
    if (this.disabled) {
      //新增disabled样式
      this.backgroundColor = '#f3f3f3'
      this.$refs.selectInput.setAttribute('disabled', 'disabled')
    } else {
      this.$refs.selectInput.removeAttribute('disabled')
      this.backgroundColor = '#fff'
    }
  },
  methods: {
    treeObjFn() {
      return {
        url: this.source.url,
        dataType: this.source.dataType || 'json',
        method: this.source.method,
        textName: this.source.text,
        codeName: this.source.code,
        childName: this.source.children,
        data: this.source.data,
        type: this.source.treeType || 'checkbox'
      }
    },
    init() {
      if (this.setObj.dataType === 'local') {
        if (this.data.length == 0) {
          this.data = JSON.parse(JSON.stringify(this.setObj.data || []))
        }
        const len = this.data.length
        if (this.setObj.choice !== true && this.setObj.tree !== true) {
          this.height = len * 25 > 200 ? 200 : len * 25 + 25
        } else {
          this.height = len * 25 > 200 ? 200 : len * 25
        }
        if (!this.clearable) {
          this.height = len * 25 > 200 ? 200 : len * 25
        }
        if (this.setObj.tree === true) {
          this.height = 200
        }
        this.countLayout()
        this.setSelectData(this.data)
      } else if (
        (this.setObj.dataType === 'json' || !this.setObj.dataType) &&
        this.setObj.tree !== true
      ) {
        this.data = []
        this.height = 50
        this.getData()
      } else if (this.setObj.tree === true) {
        this.height = 200
        this.countLayout()
      }
      this.isInit = false
    },
    // 显示隐藏
    showOpen(e, type) {
      this.$emit('inputBlur', this.textValue)
      const winH = document.body.clientHeight
      const elm = this.$refs.selectBox.getBoundingClientRect()
      if (this.disabled === true) {
        return false
      }
      this.loading = false
      if (type === true) {
        this.nomore = false
        this.domElm = {
          width: this.setObj.grid ? elm.width * 3 : elm.width,
          left: elm.left,
          top: elm.top
        }
        this.init()
        this.$refs.selectInput.focus()
        this.show = type
      } else {
        this.page = 1
        setTimeout(() => {
          this.show = type
        }, 100)
      }
      this.addRemoveDateWinToBody(type)
      // if (this.setObj.tree === true && type === true) {
      //   this.$nextTick(() => {
      //     // this.$refs.tree.refresh(false)
      //   })
      // }
      this.choiceTop = this.$refs.selectBox.offsetHeight + 5
      this.choiceBottom = 5
    },
     // 将弹框加入Body中
    addRemoveDateWinToBody(type) {
      this.$nextTick(() => {
        const body = document.querySelector('body')
        if (type) {
          // 显示
          let selectWin = this.$refs.selectWin
          let pTabName = selectWin.parentNode ? selectWin.parentNode.tagName : ''
          if (pTabName != 'body') {
            // 未加入，body，选择加入
            if (body.append) {
              body.append(selectWin)
            } else {
              body.appendChild(selectWin)
            }
          }
        } else {
          // 隐藏
          body.removeChild(body.lastChild)
        }
      })
    },
    // 初始化是否查询数据
    initSearchData() {
      let data = this.setObj.data || []
      const _val = this.value
      let _code = null
      if (this.setObj.dataType === 'local' && this.setObj.tree !== true) {
        data.forEach((v, i) => {
          if (v[this.setObj.code] === this.currentValue) {
            this.textValue = v[this.setObj.text]
          }
        })
      } else if (
        (this.setObj.dataType === 'json' || !this.setObj.dataType) &&
        this.setObj.tree !== true
      ) {
        this.getData(true)
      }
      // 多选回显示
      if (this.setObj.choice === true && this.setObj.dataType === 'local') {
        if (!_val) {
          this.clear()
          if (this.setObj.data) {
            this.setObj.data.forEach((v, i) => {
              data[i].selected = false
            })
          }
        }
        if (_val && typeof _val === 'string') {
          this.choiceSelect = []
          _code = _val.split(',')
          if (this.setObj.data) {
            this.setObj.data.forEach((v, i) => {
              _code.forEach((vo, l) => {
                if (v[this.setObj.code] == vo) {
                  data[i].selected = true
                  this.choiceSelect.push(v)
                }
              })
            })
          }
        }
      } else if (
        this.setObj.choice === true &&
        this.setObj.dataType === 'json'
      ) {
        _code = _val.split(',')
        _code.forEach((vo, l) => {
          this.getData('choice', vo)
        })
      }
    },
    // 计算层显示位置
    countLayout() {
      const winH = document.body.clientHeight
      const elm = this.$refs.selectBox.getBoundingClientRect()
      if (elm.top + this.height + 25 > winH) {
        this.where = 'up'
      } else {
        this.where = 'down'
      }
    },
    // 单选失去光标
    selectOddBlur(e) {
      if (this.blurTF === true) {
        this.showOpen(e, false)
      } else {
        this.blurTF = true
      }
    },
    // 选择
    links(e, val) {
      const _this = this
      const code = this.setObj.code
      const text = this.setObj.text
      let o = {}
      this.blurTF = false
      if (val === '') {
        this.setValue('', '')
      } else {
        this.data.forEach((v, i) => {
          if (v[code] === val) {
            o = v
          }
        })
        this.setValue(o[text], o[code])
      }
      this.showOpen(e, false)
      this.$nextTick(() => {
        this.$emit('selected', o, 'aa')
      })
    },
    // 多选选择
    choice(item, val) {
      const code = this.setObj.code
      const text = this.setObj.text
      let str = ''
      this.isDataRef = false
      if (!item.selected) {
        // 选中
        this.choiceSelect.forEach((vo, l) => {
          if (item[code] === vo[code]) {
            this.choiceSelect.splice(l, 1)
            return false
          }
        })
      } else {
        this.choiceSelect.push(item)
      }
      this.choiceSelect.forEach((v, i) => {
        if (str === '') {
          str = v[code]
        } else {
          str += ',' + v[code]
        }
      })
      this.currentValue = str
      this.$refs.selectInput.focus()
      this.$nextTick(() => {
        this.$emit('selected', this.choiceSelect)
      })
      setTimeout(() => {
        this.choiceTop = this.$refs.selectBox.offsetHeight + 5
      })
    },
    // 多选移除已选中项
    remove(event, id) {
      const _this = this
      const code = this.setObj.code
      let row = {}
      event.stopPropagation()
      this.choiceSelect.forEach((v, i) => {
        if (v[code] === id) {
          row = v
          _this.choiceSelect.splice(i, 1)
          return false
        }
      })
      this.data.forEach((v, i) => {
        if (v[code] === id) {
          v.selected = false
          return false
        }
      })
      let str = ''
      this.choiceSelect.forEach((v, i) => {
        if (str === '') {
          str = v[code]
        } else {
          str += ',' + v[code]
        }
      })
      this.currentValue = str
      setTimeout(() => {
        this.choiceTop = this.$refs.selectBox.offsetHeight + 5
      })
      this.$emit('remove', this.currentValue)
      this.$emit('change', code, row)
    },
    // 获取动态数据
    getData(type, code) {
      const _this = this
      let headers = {}
      this.setObj.data = this.source.data
      let data = JSON.parse(JSON.stringify(this.setObj.data || {}))
      data[this.setObj.page || 'pageNo'] = this.page
      data[this.setObj.pageSizeName || 'pageSize'] = this.setObj.pageSize || 20
      if (this.setObj.condition !== false) {
        data[this.setObj.condition || 'condition'] = this.searchWords || ''
      }
      if (this.setObj.headers) {
        headers = this.setObj.headers
      }
      if (type === true) {
        data[this.setObj.code] = this.currentValue
      }
      if (
        type === true &&
        this.setObj.choice === true &&
        this.setObj.dataType === 'json'
      ) {
        data[this.setObj.code] = code
      }
      this.loading = true
      this.$api({
        url: this.setObj.url,
        method: this.setObj.method || 'post',
        data: data,
        headers: headers,
        loading: false
      })
        .then(function(response) {
          // 请求成功
          let data = []
          _this.loading = false
          if (response.rows) {
            data = response.rows || []
          } else if (
            response.rows === undefined &&
            response.success === true &&
            response.result === undefined
          ) {
            data = response.object || []
            _this.isScroll = false
          } else if (
            response.rows === undefined &&
            response.success === true &&
            response.object === undefined
          ) {
            data = response.result || []
            _this.isScroll = false
          }
          if (type === true && _this.setObj.tree !== true) {
            // 初始化赋值
            _this.initSetData(data)
          } else if (
            type === 'choice' &&
            _this.setObj.choice === true &&
            _this.setObj.dataType === 'json' &&
            code !== undefined
          ) {
            _this.choiceInit(data, code)
          } else {
            _this.distantData(data)
          }
          _this.searchWords = ''
          _this.isSearch = false
          _this.$emit('loadSuccess', data)
        })
        .catch(function(error) {
          // 请求失败
          _this.searchWords = ''
        })
    },
    // 多选初始化渲染
    choiceInit(data, code) {
      data.forEach(v => {
        if (v[this.setObj.code] == code) {
          v.selected = true
          this.choiceSelect.push(v)
        }
      })
    },
    // 初始化赋值
    initSetData(data) {
      if (Array.isArray(data) !== true) {
        return false
      }
      data.forEach((v, i) => {
        if (v[this.setObj.code] === this.currentValue) {
          this.textValue = v[this.setObj.text]
        }
      })
    },
    // 远程数据处理
    distantData(data) {
      let len = 0
      // 如果是多选
      if (this.setObj.choice === true) {
        data = this.setSelectData(data)
      }
      if (this.isSearch === true) {
        this.data = data
      } else {
        this.data = this.data.concat(data)
      }
      if (data.length === 0) {
        this.nomore = true
      }
      len = this.data.length
      if (this.setObj.choice !== true && this.setObj.tree !== true) {
        this.height = len * 25 > 200 ? 200 : len * 25 + 25
      } else {
        this.height = len * 25 > 200 ? 200 : len * 25
      }
      this.countLayout()
    },
    // 多选时，已选择内容匹配
    setSelectData(data) {
      const _this = this
      const code = this.setObj.code
      const text = this.setObj.text
      data.forEach((v, i) => {
        _this.choiceSelect.forEach((vo, l) => {
          if (v[code] === vo[code]) {
            v.selected = vo.selected
          }
        })
      })
      return data
    },
    // 滚动
    scroll(e) {
      const len = this.data.length
      const h = len * 25 - 200
      const elm = this.$refs.selectList
      let scrolltop = elm.scrollTop

      if (this.setObj.tree === true || this.isScroll === false) {
        return false
      }
      if (
        scrolltop >= h &&
        this.nomore === false &&
        this.loading === false &&
        (this.setObj.dataType === 'json' || !this.setObj.dataType)
      ) {
        this.loading = true
        this.page += 1
        setTimeout(() => {
          this.getData()
        }, 500)
      }
    },
    // 检索
    search(e) {
      let len = 0
      let data = []
      this.searchWords = this.textValue
      if (this.setObj.choice === true) {
        this.searchWords = this.choiceTextValue
      }
      clearTimeout(this.clearTimes)
      this.clearTimes = setTimeout(() => {
        this.page = 1
        this.isSearch = true
        this.nomore = false
        if (this.setObj.dataType !== 'local') {
          this.getData()
        } else {
          this.setObj.data.forEach((v, i) => {
            if (v[this.setObj.text].indexOf(this.searchWords) > -1) {
              data.push(v)
            }
          })
          if (this.searchWords === '') {
            data = this.setObj.data
          }
          data.forEach((v, i) => {
            this.choiceSelect.forEach((vo, l) => {
              if (v[this.setObj.code] === vo[this.setObj.code]) {
                data[i].selected = true
              }
            })
          })
          this.data = data
          len = this.data.length
          if (len === 0) {
            this.nomore = true
          } else {
            this.nomore = false
          }
          if (this.setObj.choice !== true && this.setObj.tree !== true) {
            this.height = len * 25 > 200 ? 200 : len * 25 + 25
          } else {
            this.height = len * 25 > 200 ? 200 : len * 25 + (len === 0 ? 25 : 0)
          }
        }
      }, 500)
    },
    choiceChange() {
      const w = this.$refs.selectFlexBox.offsetWidth - 140
      let _w = this.strWidth(this.choiceTextValue)
      if (_w < w) {
        this.choiceWidth = _w
      } else {
        this.choiceWidth = w
      }
      setTimeout(() => {
        this.choiceTop = this.$refs.selectBox.offsetHeight + 5
      })
      this.search()
    },
    valueChange() {},
    // 计算字符串宽度
    strWidth(text) {
      let sensor = document.createElement('a')
      sensor.innerHTML = text
      let body = document.getElementsByTagName('body')[0]
      body.appendChild(sensor)
      let width = sensor.offsetWidth
      body.removeChild(sensor)
      return width
    },
    // 树选择触发
    treeChange(row, nodes) {
      const _this = this
      const type = this.source.treeType
      let text = ''
      let code = ''
      if (type === 'normal') {
        text = row[_this.setObj.text] || ''
        code = row[_this.setObj.code] || ''
      } else {
        nodes.forEach((v, i) => {
          if (text === '') {
            text = v[_this.setObj.text]
            code = v[_this.setObj.code]
          } else {
            text += ',' + v[_this.setObj.text]
            code += ',' + v[_this.setObj.code]
          }
        })
      }
      this.setValue(text, code)
      if (row) {
        this.$nextTick(() => {
          this.$emit('selected', row, nodes)
          this.$emit('change', code, row)
        })
      }
      if (type === 'normal') {
        this.showOpen(null, false)
      }
    },
    // 清空
    clear() {
      this.setValue('', '')
      if (this.setObj.tree === true) {
        this.checkRowKeys = []
        this.$refs.tree.clearCheckboxRow()
      }
      if (this.setObj.choice === true) {
        this.choiceSelect = []
      }
      this.treeChange({}, [])
    },
    // 多次赋值屏蔽
    setValue(text, code) {
      if (text !== this.textValue) {
        this.isValue = true
        this.isSource = true
      }
      this.textValue = text
      this.currentValue = code
      this._val = code
    },
    // 阻止冒泡
    forbidBubbling(e) {
      e.stopPropagation()
    }
  },
  watch: {
    value: {
      handler(n, o) {
        if (this.isValue === true) {
          this.isValue = false
          return false
        }
        this.currentValue = n
        if (this.setObj.isReq === true && this.setObj.tree !== true) {
          this.initSearchData()
        }
      },
      deep: true
    },
    currentValue: {
      handler(news, old) {
        if (news === '') {
          this.textValue = ''
        }
        if (this.isInit === false) {
          this.isValue = true
          this.$emit('input', news)
          if (this.setObj.tree !== true) {
            this.$emit('change', news)
            // this.$emit('selected', news)
          }
        } else {
          // this.$emit('change', news)
          this.$emit('input', news)
          this.isInit = false
        }
      },
      deep: true
    },
    source: {
      handler(news, old) {
        const val = news.value
        let n = JSON.parse(JSON.stringify(news))
        if (this.isSource === true) {
          this.isSource = false
          return false
        }
        //

        if (news.dataType !== 'local') {
          //   this.treeObj.data = news.data
          // this.data = news.data
          n.data = this.data
        }
        // this.setObj = {
        //   ...n
        // }
        // if (typeof val === 'object' && this.setObj.isReq !== true) {
        // this.textValue = val.name
        // this.currentValue = val.code
        // this.$forceUpdate()
        // }
      },
      deep: true
    },
    'source.value': {
      handler(n, o) {
        if (typeof n === 'object' && this.setObj.isReq !== true) {
          this.textValue = n.name
          this.currentValue = n.code
          this.$forceUpdate()
        }
      },
      deep: true
    },
    'source.data': {
      handler(n, o) {
        if (this.isDataRef === false) {
          this.isDataRef = true
          return false
        }
        if (this.setObj.dataType === 'local') {
          this.treeObj.data = n
          this.data = n
        }
      },
      deep: true
    },
    disabled: {
      handler(o) {
        if (this.disabled) {
          //新增disabled样式
          this.backgroundColor = '#f3f3f3'
          this.$refs.selectInput.setAttribute('disabled', 'disabled')
        } else {
          this.$refs.selectInput.removeAttribute('disabled')
          this.backgroundColor = '#fff'
        }
      }
    }
  },
  destroyed() {}
}
</script>

<style lang="scss" scoped>
.vxe-select-option {
  max-width: 100% !important;
}
.select-choice__ul {
  cursor: pointer;
}
.select_grid_contanier {
  ul {
    height: 22px;
    line-height: 22px;
    width: 100%;
    font-size: 12px;

    li {
      float: left;
      display: flex;
      margin-top: 5px;
      cursor: pointer;
    }

    li:hover {
      color: var(--base-acv);
    }
  }
}
</style>